﻿using System;
using System.Net.Sockets;
using System.Text;
using System.Threading;

namespace ModbusTcp
{
    public class ModbusTcpClient
    {
        private TcpClient _client;
        private NetworkStream _stream;
        private readonly string _ipAddress;
        private readonly int _port;
        private readonly CancellationTokenSource _cancellationTokenSource = new();

        public event Action<string> OnLog;

        public ModbusTcpClient(string ipAddress, int port)
        {
            _ipAddress = ipAddress;
            _port = port;
        }

        public bool IsConnected => _client?.Connected ?? false;

        public async void Start()
        {
            try
            {
                _client = new TcpClient();
                await _client.ConnectAsync(_ipAddress, _port);
                _stream = _client.GetStream();

                OnLog?.Invoke($"Connected to {_ipAddress}:{_port}");

                // Start listening for messages
                _ = ListenForMessages(_cancellationTokenSource.Token);
            }
            catch (Exception ex)
            {
                OnLog?.Invoke($"Error starting client: {ex.Message}");
            }
        }

        public void Stop()
        {
            _cancellationTokenSource.Cancel();

            _stream?.Close();
            _client?.Close();

            OnLog?.Invoke("Client stopped.");
        }

        private async Task ListenForMessages(CancellationToken cancellationToken)
        {
            var buffer = new byte[256];

            try
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    if (_stream == null) continue;

                    var bytesRead = await _stream.ReadAsync(buffer, cancellationToken);
                    if (bytesRead > 0)
                    {
                        var data = new byte[bytesRead];
                        Array.Copy(buffer, data, bytesRead);

                        OnLog?.Invoke($"Received: {BitConverter.ToString(data)}");
                        ModbusPacketParser.Parse(data);
                    }
                    else
                    {
                        OnLog?.Invoke("Disconnected.");
                        Stop();
                    }
                }
            }
            catch (Exception ex)
            {
                OnLog?.Invoke($"Error: {ex.Message}");
            }
        }

        public async Task Send(byte[] data)
        {
            if (!IsConnected) throw new InvalidOperationException("Not connected.");

            try
            {
                await _stream.WriteAsync(data);
                OnLog?.Invoke($"Sent: {BitConverter.ToString(data)}");
            }
            catch (Exception ex)
            {
                OnLog?.Invoke($"Send error: {ex.Message}");
            }
        }
    }
}
